#include "myc.h"

struct func *funcs;

struct func *add_func(int spe) {
	struct func *me=calloc(1,sizeof(*me));
	struct type *btyp;
	me->type.bt=pass_type();
	while (tokis(pass,"*")) {
		me->type.pt++;
		pass=pass->next;
	}
	if (tokis(pass,"&")) {
		me->type.lv=1;
		pass=pass->next;
	}
	if (!is_letter(*pass->str) || !tokis(pass->next,"(")) error("invalid func name");
	char *tmpname=calloc(1,2+endbr(pass->next,NULL)->str-pass->str);
	strncat(tmpname,pass->str,pass->len);
	pass=pass->next->next;
	strcat(tmpname,"(");
	while (!tokis(pass,")")) {
		btyp=pass_type();
		strcat(tmpname,btyp->name);
		while (tokis(pass,"*")) {
			pass=pass->next;
			strcat(tmpname,"*");
		}
		if (tokis(pass,"&")) {
			pass=pass->next;
			strcat(tmpname,"&");
		}
		if (is_letter(*pass->str)) pass=pass->next;
		if (tokis(pass,",")) {
			pass=pass->next;
			strcat(tmpname,",");
			if (tokis(pass,")")) error("missing argument");
		} else if (!tokis(pass,")")) error("missing ,");
	}
	pass=pass->next;
	strcat(tmpname,")");
	me->name=strdup(tmpname);
	free(tmpname);
	me->spe=spe;
	struct func *tmp;
	for (tmp=funcs;tmp;tmp=tmp->next) if (strcmp(tmp->name,me->name)==0) {
		if (me->type.pt==tmp->type.pt && me->type.bt==tmp->type.bt) {
			free(me->name);
			free(me);
			return tmp;
		} else error("double declaration");
	}
	me->next=funcs;
	return funcs=me;
}

int check_func(struct tree *tree,struct func *func) {
	int i,ret=3;
	char *name=func->name;
	struct vtype same={tsame,0};
	if (strncmp(name,tree->to.token->str,tree->to.token->len)) return 0;
	name+=tree->to.token->len;
	if (*name++!='(') return 0;
	tree=tree->down;
	if (!tree) return *name==')' && ret;
	while (1) {
		if (!strncmp(name,"same",4)) {
			struct vtype vt=tree->type;
			name+=4;
			name+=i=strspn(name,"*");
			if ((vt.pt-=i)<0) return 0;
			if (ret>2) ret=2;
			if (same.bt==tsame) same=vt;
			else if (same.bt!=vt.bt || same.pt!=vt.pt) return 0;
		} else if (!strncmp(name,"void",4)) {
			name+=4;
			name+=i=strspn(name,"*");
			if (i>tree->type.pt) return 0;
			ret=1;
		} else {
			char *arg=tree->type.bt->name;
			if (strncmp(name,arg,strlen(arg))) return 0;
			name+=strlen(arg);
			if (strspn(name,"*")!=tree->type.pt) return 0;
			name+=tree->type.pt;
		}
		tree=tree->next;
		if (*name=='&') name++;
		if (!tree) return *name==')' && ret;
		else if (*name++!=',') return 0;
	}
}

struct vtype find_same(struct tree *tree,struct func *func) {
	tree=tree->down;
	struct vtype same={tsame,0};
	char *name=strchr(func->name,'(')+1;
	while (tree) {
		if (!strncmp(name,"same",4)) {
			same=tree->type;
			name+=4;
			same.pt-=strspn(name,"*");
			return same;
		} else {
			name=strchr(name,',')+1;
			tree=tree->next;
		}
	}
	treerror(tree,"cannot find same");
	return same;
}

struct func *find_func(struct tree *tree) {
	struct func *flist=funcs,*ret=NULL;
	int i,pri=0;
	while (flist) {
		if ((i=check_func(tree,flist))>pri) {
			pri=i;
			ret=flist;
		}
		flist=flist->next;
	}
	return ret;
}

struct func *get_func(char *str) {
	struct func *flist=funcs;
	while (flist) {
		if (strcmp(str,flist->name)==0) return flist;
		flist=flist->next;
	}
	return NULL;
}

void freefuncs() {
	struct func *tmp;
	while (funcs) {
		if (!funcs->loc) if (funcs->spe==0 || (funcs->spe>2 && funcs->spe<6)) ext_func(funcs->name);
		free(funcs->name);
		tmp=funcs->next;
		free(funcs);
		funcs=tmp;
	}
}
